﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using NTLib.Pipe.Containers;
using NTLib.Pipe.Descriptors;
using NTLib.Pipe.Exceptions;
using NTLib.Core.Tasks;
using NTLib.Pipe.UTest.TestData;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace NTLib.Pipe.UTest.Containers
{
    [TestClass]
    public class PipeUsingSourceUTest
    {
        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void PipeUsingSource_Creation()
        {
            var pipe = new PipeUsingSource<DataStruct>();

            MemoryPipeSource<DataStruct> memorySource = new MemoryPipeSource<DataStruct>();
            memorySource.InitializeAsync(LocalTargetDescription.Instance, PipeType.TwoWay).Wait();

            Assert.IsNotNull(pipe);


            Guid id;
            try
            {
                id = pipe.Id;
            }
            catch (PipeNotInitializedException)
            {

            }

            Assert.AreEqual(memorySource.CanPopItem, true);
            Assert.AreEqual(memorySource.CanPushItem, true);

            DataStruct str = new DataStruct();
            id = Guid.NewGuid();
            str.Data = id.ToString();

            try
            {
                Assert.AreEqual(pipe.PipeType, PipeType.TwoWay);
                Assert.Fail("Mustn't mot be possible to initialize twice the same instance");
            }
            catch (PipeNotInitializedException)
            {
            }
            catch
            {
                Assert.Fail("Expecting PipeNotInitializedException exception");
            }

            try
            {
                pipe.SendAsync(str).Wait();
                Assert.Fail("Mustn't mot be possible to initialize twice the same instance");
            }
            catch (AggregateException ex)
            {
                Assert.IsNotNull(ex.InnerException);
                Assert.AreEqual(ex.InnerExceptions.Count, 1);
                Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException), "We attend to received a PipeNotInitializedException");
            }
            catch
            {
                Assert.Fail("Expecting PipeNotInitializedException exception");
            }


            //try
            //{
            //    pipe.PushAsync<bool>(str).Wait();
            //    Assert.Fail("Mustn't mot be possible to initialize twice the same instance");
            //}
            //catch (AggregateException ex)
            //{
            //    Assert.IsNotNull(ex.InnerException);
            //    Assert.AreEqual(ex.InnerExceptions.Count, 1);
            //    Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException), "We attend to received a PipeNotInitializedException");
            //}
            //catch
            //{
            //    Assert.Fail("Expecting PipeNotInitializedException exception");
            //}


            try
            {
                pipe.PopAsync().Wait();
                Assert.Fail("Mustn't mot be possible to initialize twice the same instance");
            }
            catch (AggregateException ex)
            {
                Assert.IsNotNull(ex.InnerException);
                Assert.AreEqual(ex.InnerExceptions.Count, 1);
                Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException), "We attend to received a PipeNotInitializedException");
            }
            catch
            {
                Assert.Fail("Expecting PipeNotInitializedException exception");
            }


            try
            {
                pipe.ClosePipeAsync().Wait();
                Assert.Fail("Mustn't mot be possible to initialize twice the same instance");
            }
            catch (AggregateException ex)
            {
                Assert.IsNotNull(ex.InnerException);
                Assert.AreEqual(ex.InnerExceptions.Count, 1);
                Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException), "We attend to received a PipeNotInitializedException");
            }
            catch
            {
                Assert.Fail("Expecting PipeNotInitializedException exception");
            }

            pipe.SetSource(memorySource, PipeType.OneWay);

            Assert.AreNotEqual(pipe.Id, Guid.Empty);
            Assert.AreEqual(pipe.PipeType, PipeType.OneWay);
        }

        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void PipeUsingSource_PipeCloseStatus()
        {
            var pipe = new PipeUsingSource<DataStruct>();

            Assert.AreEqual(pipe.Status, PipeStatus.NotInitialize);

            MemoryPipeSource<DataStruct> memorySource = new MemoryPipeSource<DataStruct>();
            Assert.AreEqual(memorySource.Status, PipeStatus.NotInitialize);

            memorySource.InitializeAsync(LocalTargetDescription.Instance, PipeType.TwoWay).Wait();

            Assert.AreEqual(memorySource.Status, PipeStatus.Ready);

            pipe.SetSource(memorySource, PipeType.TwoWay);
            Assert.AreEqual(pipe.Status, PipeStatus.Ready);


            pipe.ClosePipeAsync().Wait();

            Assert.AreEqual(pipe.Status, PipeStatus.Closed);
            Assert.AreEqual(memorySource.Status, PipeStatus.Closed);

            try
            {
                pipe.PopAsync().Wait();
            }
            catch (AggregateException ex)
            {
                Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException));
            }

            //try
            //{
            //    pipe.PushAsync<bool>(new DataStruct()).Wait();
            //}
            //catch (AggregateException ex)
            //{
            //    Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException));

            //}

            try
            {
                pipe.SendAsync(new DataStruct()).Wait();
            }
            catch (AggregateException ex)
            {
                Assert.IsInstanceOfType(ex.InnerException, typeof(PipeNotInitializedException));

            }

        }

        //[TestCategory("Pipe.Source")]
        //[TestMethod]
        //public void PipeUsingSource_PushAndPop()
        //{
        //    using (new TestPipeManager())
        //    {
        //        var pipe = new PipeUsingSource<DataStruct>();

        //        MemoryPipeSource<DataStruct> memorySource = new MemoryPipeSource<DataStruct>();
        //        memorySource.InitializeAsync(LocalTargetDescription.Instance, PipeType.TwoWay).Wait();

        //        pipe.SetSource(memorySource, PipeType.TwoWay);

        //        DataStruct str = new DataStruct();
        //        Guid id = Guid.NewGuid();
        //        str.Data = id.ToString();

        //        var taskPush = pipe.PushAsync<bool>(str);

        //        var result = pipe.PopAsync().WaitResult();

        //        Assert.IsTrue(taskPush.IsCompleted);
        //        Assert.IsFalse(taskPush.IsCanceled);
        //        Assert.IsFalse(taskPush.IsFaulted);
        //        Assert.IsTrue(taskPush.Result);

        //        Assert.IsNotNull(result);
        //        Assert.AreEqual(result.Data, id.ToString());
        //    }
        //}

        //[TestCategory("Pipe.Source")]
        //[TestMethod]
        //public void PipeUsingSource_PushAndPop_WithCancel()
        //{
        //    var pipe = new PipeUsingSource<DataStruct>();

        //    MemoryPipeSource<DataStruct> memorySource = new MemoryPipeSource<DataStruct>();
        //    memorySource.InitializeAsync(LocalTargetDescription.Instance, PipeType.TwoWay).Wait();

        //    pipe.SetSource(memorySource, PipeType.TwoWay);

        //    DataStruct str = new DataStruct();
        //    Guid id = Guid.NewGuid();
        //    str.Data = id.ToString();

        //    CancellationTokenSource source = new CancellationTokenSource();

        //    var taskPush = pipe.PushAsync<bool>(str, source.Token);
        //    source.CancelAfter(10);

        //    try
        //    {
        //        taskPush.Wait();
        //    }
        //    catch (AggregateException ex)
        //    {
        //        Assert.IsInstanceOfType(ex.InnerException, typeof(OperationCanceledException));
        //    }

        //    Assert.IsTrue(taskPush.IsCompleted);
        //    Assert.IsTrue(taskPush.IsCanceled);
        //    Assert.IsFalse(taskPush.IsFaulted);

        //    source = new CancellationTokenSource();
        //    source.CancelAfter(2000);

        //    var result = pipe.PopAsync().WaitResult();
        //    Assert.IsNotNull(result);
        //    Assert.AreEqual(result.Data, id.ToString());

        //    try
        //    {
        //        pipe.PopAsync(source.Token).WaitResult();
        //    }
        //    catch (AggregateException ex)
        //    {
        //        Assert.IsInstanceOfType(ex.InnerException, typeof(OperationCanceledException));
        //    }
        //}


        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void PipeUsingSource_SendAndPop()
        {
            var pipe = new PipeUsingSource<DataStruct>();

            MemoryPipeSource<DataStruct> memorySource = new MemoryPipeSource<DataStruct>();
            memorySource.InitializeAsync(LocalTargetDescription.Instance, PipeType.TwoWay).Wait();

            pipe.SetSource(memorySource, PipeType.TwoWay);

            DataStruct str = new DataStruct();
            Guid id = Guid.NewGuid();
            str.Data = id.ToString();

            CancellationTokenSource source = new CancellationTokenSource();
            source.CancelAfter(5000);

            var taskPush = pipe.SendAsync(str, source.Token);
            taskPush.Wait();

            source = new CancellationTokenSource();
            source.CancelAfter(5000);

            var result = pipe.PopAsync(source.Token).WaitResult();

            Assert.IsTrue(taskPush.IsCompleted);
            Assert.IsFalse(taskPush.IsCanceled);
            Assert.IsFalse(taskPush.IsFaulted);

            Assert.IsNotNull(result);
            Assert.AreEqual(result.Data, id.ToString());
        }
    }
}
